package com.xscha.soyue.common.core.json;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.MapType;
import com.xscha.soyue.common.core.exception.SoyRuntimeException;
import com.xscha.soyue.common.core.exception.status.CommonStatusCode;
import jakarta.servlet.http.HttpServletResponse;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.*;

/**
 * json与bean转换对象 -->Jackson实现
 *
 * @author Vondser
 * @version V2024.1.0
 */
@Slf4j
@NoArgsConstructor
@SuppressWarnings("unchecked")
public class JacksonBeanJsonService implements BeanJsonService {

	private ObjectMapper mapper = null;

	public JacksonBeanJsonService(ObjectMapper mapper) {
		this.mapper = mapper;
	}

	public synchronized void setMapper(ObjectMapper mapper) {
		this.mapper = mapper;
	}

	@Override
	public String toJson(Object object) throws SoyRuntimeException {
		try {
			return mapper.writeValueAsString(object);
		}
		catch (JsonProcessingException e) {
			throw new SoyRuntimeException(CommonStatusCode.DATA_CONVERT_ERROR,
					String.format("对象[%s]转换为Json失败", object.getClass()), e);
		}
	}

	@Override
	public <T> List<T> toList(String source, Class<T> clz) throws SoyRuntimeException {
		try {
			if (StrUtil.isBlank(source)) {
				return null;
			}
			JavaType javaType = mapper.getTypeFactory().constructParametricType(ArrayList.class, clz);
			return mapper.readValue(source, javaType);
		}
		catch (JsonProcessingException e) {
			throw new SoyRuntimeException(CommonStatusCode.DATA_CONVERT_ERROR,
					String.format("JSON %s转换为List<%s>失败", source, clz.getName()), e);
		}
	}

	@Override
	public <T> Set<T> toSet(String source, Class<T> clz) throws SoyRuntimeException {
		try {
			if (StrUtil.isBlank(source)) {
				return null;
			}
			JavaType javaType = mapper.getTypeFactory().constructParametricType(HashSet.class, clz);
			return mapper.readValue(source, javaType);
		}
		catch (JsonProcessingException e) {
			throw new SoyRuntimeException(CommonStatusCode.DATA_CONVERT_ERROR, String.format("[%s]转换为Set失败", source),
					e);
		}
	}

	@Override
	public Map<String, Object> toMap(Object obj) throws SoyRuntimeException {
		if (obj == null) {
			return null;
		}
		if (obj instanceof CharSequence) {
			return toObject(obj.toString(), LinkedHashMap.class);
		}
		try {
			JavaType javaType = mapper.getTypeFactory()
				.constructParametricType(HashMap.class, String.class, Object.class);
			return mapper.convertValue(obj, javaType);
		}
		catch (Exception e) {
			throw new SoyRuntimeException(CommonStatusCode.DATA_CONVERT_ERROR,
					String.format("[%s]转换为Map失败", obj.toString()), e);
		}
	}

	@Override
	public <T> T parseObject(byte[] bytes, Class<T> clazz) throws SoyRuntimeException {
		if (ArrayUtil.isEmpty(bytes)) {
			return null;
		}
		try {
			return mapper.readValue(bytes, clazz);
		}
		catch (Exception e) {
			throw new SoyRuntimeException(CommonStatusCode.DATA_CONVERT_ERROR, String.format("[%s]转换为Object失败"), e);
		}
	}

	@Override
	public <T> T parseObject(String text, Class<T> clazz) throws SoyRuntimeException {
		if (StrUtil.isEmpty(text)) {
			return null;
		}
		try {
			return mapper.readValue(text, clazz);
		}
		catch (Exception e) {
			throw new SoyRuntimeException(CommonStatusCode.DATA_CONVERT_ERROR, String.format("[%s]转换为Object失败"), e);
		}
	}

	@Override
	public <T> T parseObject(String text, TypeReference<T> typeReference) throws SoyRuntimeException {
		try {
			return mapper.readValue(text, typeReference);
		}
		catch (Exception e) {
			throw new SoyRuntimeException(CommonStatusCode.DATA_CONVERT_ERROR, String.format("[%s]转换为Object失败"), e);
		}
	}

	@Override
	public <T> List<T> parseArray(String text, Class<T> clazz) throws SoyRuntimeException {
		if (StrUtil.isEmpty(text)) {
			return new ArrayList<>();
		}

		try {
			return mapper.readValue(text, mapper.getTypeFactory().constructCollectionType(List.class, clazz));
		}
		catch (Exception e) {
			throw new SoyRuntimeException(CommonStatusCode.DATA_CONVERT_ERROR, String.format("[%s]转换为Array失败"), e);
		}
	}

	@Override
	public String toJsonString(Object object) throws SoyRuntimeException {
		try {
			return mapper.writeValueAsString(object);
		}
		catch (JsonProcessingException e) {
			throw new SoyRuntimeException(CommonStatusCode.DATA_CONVERT_ERROR, String.format("[%s]转换为字符串失败"), e);
		}
	}

	@Override
	public JsonNode parseTree(String text) throws SoyRuntimeException {
		try {
			return mapper.readTree(text);
		}
		catch (Exception e) {
			throw new SoyRuntimeException(CommonStatusCode.DATA_CONVERT_ERROR, String.format("[%s]转换为Tree失败"), e);
		}
	}

	@Override
	public <K, V> Map<K, V> toMap(String source, Class<K> klz, Class<V> vlz) throws SoyRuntimeException {
		try {
			if (StrUtil.isBlank(source)) {
				return null;
			}
			MapType mapType = mapper.getTypeFactory().constructMapType(HashMap.class, klz, vlz);
			return mapper.readValue(source, mapType);
		}
		catch (JsonProcessingException e) {
			throw new SoyRuntimeException(CommonStatusCode.DATA_CONVERT_ERROR, String.format("[%s]转换为Map失败", source),
					e);
		}
	}

	@Override
	public <T> T toObject(String source, Class<T> clz) throws SoyRuntimeException {
		try {
			return StrUtil.isNotBlank(source) ? mapper.readValue(source, clz) : null;
		}
		catch (JsonProcessingException e) {
			throw error(source, clz, e);
		}
	}

	@Override
	public <T> T toObject(String source, Type target) throws SoyRuntimeException {
		try {
			return StrUtil.isNotBlank(source)
					? mapper.readValue(source, mapper.getTypeFactory().constructType(target)) : null;
		}
		catch (JsonProcessingException e) {
			String message = String.format(CommonStatusCode.DATA_CONVERT_ERROR.getCode() + "：source--[%s] 转换为 [%s] 失败",
					source, target.getTypeName());
			throw new SoyRuntimeException(CommonStatusCode.DATA_CONVERT_ERROR, message, e);
		}
	}

	@Override
	public <T> T toObject(String source, TypeReference<T> type) throws SoyRuntimeException {
		try {
			return StrUtil.isNotBlank(source) ? mapper.readValue(source, type) : null;
		}
		catch (JsonProcessingException e) {
			throw error(source, type.getType().getClass(), e);
		}
	}

	@Override
	public <T> T toObject(String source, Class<?> parametrized, Class<?>... parameterClasses) {
		try {
			JavaType javaType = mapper.getTypeFactory().constructParametricType(parametrized, parameterClasses);
			return mapper.readValue(source, javaType);
		}
		catch (JsonProcessingException e) {
			String message = String.format(CommonStatusCode.DATA_CONVERT_ERROR.getCode() + "：source--[%s] 转换为 [%s] 失败",
					source, parametrized.getName());
			throw new SoyRuntimeException(CommonStatusCode.DATA_CONVERT_ERROR, message, e);
		}
	}

	@Override
	public Object getMapper() {
		return mapper;
	}

	private static <T> SoyRuntimeException error(String source, Class<T> target, Throwable cause) {
		String message = String.format(CommonStatusCode.DATA_CONVERT_ERROR.getCode() + "：source--[%s] 转换为 [%s] 失败",
				source, target.getName());
		return new SoyRuntimeException(CommonStatusCode.DATA_CONVERT_ERROR, message, cause);
	}

	public void writeValue(HttpServletResponse response, Object o) {
		response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
		try {
			mapper.writeValue(response.getOutputStream(), o);
		}
		catch (IOException e) {
			log.error(e.getMessage(), e);
		}
	}

	public <T> T readValue(String json, Class<T> tClass) {
		try {
			return mapper.readValue(json, tClass);
		}
		catch (JsonProcessingException e) {
			log.error(e.getMessage(), e);
			return null;
		}
	}

}
